﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     SMLib\Automata\IA\SUL_Emulator_IA.cs
 * Description  :     Emulator of a black box IA
 * Change log   :     -Feb 2013:  Initial version
 * To Do        :     
 ************************************************************************/
using System;
using System.Collections.Generic;
using SMLib.Automata.Base;

namespace SMLib.Automata.IA
{
    /// <summary>
    /// Emulator of a black box IA
    /// </summary>
    public class SUL_Emulator_IA : IBlackBox_IA
    {
        InterfaceAutomaton machine;
        InterfaceAutomaton.IA_State currentNode;
        Random rnd;

        int numberOfResets, numberOfSteps;
        
        /// <summary>
        /// Number of performed resets
        /// </summary>
        public int NumberOfResets
        {
        	get
        	{
        		return numberOfResets;
        	}
        }
        
        /// <summary>
        /// Number of transitions passed
        /// </summary>
        public int NumberOfSteps
        {
        	get
        	{
        		return NumberOfSteps;
        	}
        }
        
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="underlieng_machine">the underlaying Mealy machine</param>
        public SUL_Emulator_IA(InterfaceAutomaton underlieng_machine)
        {
        	numberOfResets=0;
        	numberOfSteps=0;
            machine = underlieng_machine;
            currentNode = (InterfaceAutomaton.IA_State)machine.InitialState;
            rnd = new Random();
        }


        #region IBlackBox_IOLTS Members

        /// <summary>
        /// Reset the system (to its initial state)
        /// </summary>
        public void Reset()
        {
        	numberOfResets++;
            currentNode = (InterfaceAutomaton.IA_State)machine.InitialState;
			//Console.Write ("$0___");
        }


        /// <summary>
        /// Perform an input (i.e. controllable action) on the system
        /// <remarks>Convention: if the action is not enabled, it throws an exception</remarks>
        /// </summary>
        /// <param name="input"></param>
        public void Perform(Automata.Base.ISymbol input)
        {
        	numberOfSteps++;
        	
            //bool labelEq = false;
            foreach (InterfaceAutomaton.IA_Step tr in currentNode.Transitions)
            {
                if (input.Equals(tr.Label))
                {
                    //labelEq = true;
                    if (tr.StepType == InterfaceAutomaton.IA_Step.Step_Type.ACTION)
                    {
                        currentNode = (InterfaceAutomaton.IA_State)tr.Destination;
                        return;
                    }
                }
            }
            //if (labelEq)
            //    throw new Exception("This is an event not an action!");
            //else
			//Console.Write ("$3_"+input+"__");
            throw new NotEnabledActionException("This action is not enabled!");
        }

        /// <summary>
        /// Check whether any output (observable event) exists or not
        /// </summary>
        /// <returns>true if it exists, false in other case</returns>
        public bool EventAvailable()
        {
            foreach (InterfaceAutomaton.IA_Step tr in currentNode.Transitions)
            {
                if (tr.StepType == InterfaceAutomaton.IA_Step.Step_Type.EVENT)
                {
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// Check existance of event and return the event (if any)
        /// </summary>
        /// <returns>the observed event, null if no event is observed</returns>
        public Automata.Base.ISymbol Event()
        {
            int numberOfObs = 0;
            foreach (InterfaceAutomaton.IA_Step tr in currentNode.Transitions)
            {
                if (tr.StepType == InterfaceAutomaton.IA_Step.Step_Type.EVENT)
                {
                    numberOfObs++;
                }
            }

            if (numberOfObs == 0)
            {
                //throw new Exception("No event is available!");
				return null;
            }

            numberOfSteps++;
            int sel = rnd.Next(numberOfObs);
            InterfaceAutomaton.IA_Step selTr = null;
            numberOfObs = 0;
            foreach (InterfaceAutomaton.IA_Step tr in currentNode.Transitions)
            {
                if (tr.StepType == InterfaceAutomaton.IA_Step.Step_Type.EVENT)
                {
                    if (numberOfObs == sel)
                    {
                        selTr = tr;
                        break;
                    }
                    numberOfObs++;
                }
            }
            currentNode = (InterfaceAutomaton.IA_State)(selTr.Destination);            
            return (ISymbol)selTr.Label;
        }


        /// <summary>
        /// The input alphabet of the IA
        /// </summary>
        public Alphabet InputAlphabet
        {
            get { return machine.InputAlphabet; }
        }

        #endregion
    }
}

